﻿/*********************************************************************************
*
* Copyright (c) ReflectSoftware, Inc. All rights reserved. 
*
* See License.rtf in the solution root for license information.
*
**********************************************************************************/

using System;
using System.Text;
using System.Reflection;
using System.Diagnostics;

using NLog;
using NLog.Targets;

using ReflectSoftware.Insight;
using ReflectSoftware.Insight.Common;

using RI.Utils.ExceptionManagement;

namespace ReflectSoftware.Insight.Extensions.NLog
{
    /// <summary>
    /// Redirect all NLog messages to ReflectInsight
    /// </summary>
    [Target("ReflectInsight")]
    public sealed class NLogTarget : TargetWithLayout
    {
        private readonly static String FLine;

        private ReflectInsight FReflectInsight;
        private Boolean FDisplayLevel;
        private Boolean FDisplayLocation;

        public String InstanceName { get; set; }
        public String DisplayLevel { get; set; }
        public String DisplayLocation { get; set; }

        //---------------------------------------------------------------------
        /// <summary>
        /// Initializes the <see cref="NLogTarget"/> class.
        /// </summary>
        static NLogTarget()
        {
            FLine = String.Format("{0,40}", String.Empty).Replace(" ", "-");
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Initializes a new instance of the <see cref="NLogTarget"/> class.
        /// </summary>
        public NLogTarget()
        {
            InstanceName = String.Empty;
            DisplayLevel = String.Empty;
            DisplayLocation = String.Empty;
            FReflectInsight = null;
            RIEventManager.OnServiceConfigChange += DoOnConfigChange;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Initializes the target.
        /// </summary>
        protected override void InitializeTarget()
        {
            base.InitializeTarget();
            OnConfigChange();
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Closes the target.
        /// </summary>
        protected override void CloseTarget()
        {
            RIEventManager.OnServiceConfigChange -= DoOnConfigChange;
            base.CloseTarget();
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Does the on config change.
        /// </summary>
        private void DoOnConfigChange()
        {
            OnConfigChange();
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Called when [config change].
        /// </summary>
        private void OnConfigChange()
        {
            try
            {
                lock (this)
                {                    
                    FReflectInsight = RILogManager.Get(InstanceName) ?? RILogManager.Default;

                    FDisplayLevel = DisplayLevel.ToLower().Trim() == "true";
                    FDisplayLocation = DisplayLocation.ToLower().Trim() == "true";
                }
            }
            catch (Exception ex)
            {
                RIExceptionManager.Publish(ex, "Failed during: NLogTarget.OnConfigChange()");
            }
        }

        //--------------------------------------------------------------------- 
        /// <summary>
        /// Sends the message.
        /// </summary>
        /// <param name="mType">Type of the m.</param>
        /// <param name="logEvent">The log event.</param>
        private void SendMessage(MessageType mType, LogEventInfo logEvent)
        {
            // build details
            StringBuilder sb = null;

            if (logEvent.Exception != null)
            {
                sb = new StringBuilder();
                sb.Append(ExceptionBasePublisher.ConstructIndentedMessage(logEvent.Exception));
                sb.AppendLine();
                sb.AppendLine();
            }

            if (FDisplayLevel || FDisplayLocation)
            {
                sb = sb ?? new StringBuilder();

                sb.AppendLine("NLog Details:");
                sb.AppendLine(FLine);

                if (FDisplayLevel)
                    sb.AppendFormat("{0,10}: {1}{2}", "Level", logEvent.Level.Name.ToUpper(), Environment.NewLine);

                StackFrame frame = logEvent.UserStackFrame;
                if (FDisplayLocation && logEvent.UserStackFrame != null)
                {
                    MethodBase method = frame.GetMethod();

                    String className = method != null ? method.ReflectedType.FullName: null;
                    String methodName = method != null ? method.Name : null;
                    String fileName = frame.GetFileName();

                    if (className != null) sb.AppendFormat("{0,10}: {1}{2}", "ClassName", className, Environment.NewLine);
                    if (methodName != null) sb.AppendFormat("{0,10}: {1}{2}", "MethodName", methodName, Environment.NewLine);
                    if (fileName != null) sb.AppendFormat("{0,10}: {1}{2}", "FileName", fileName, Environment.NewLine);
                    if (fileName != null) sb.AppendFormat("{0,10}: {1}{2}", "LineNumber", frame.GetFileLineNumber(), Environment.NewLine);
                }
            }

            String details = sb != null ? sb.ToString() : null;
            FReflectInsight.Send(mType, logEvent.FormattedMessage, details);
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Writes the specified log event.
        /// </summary>
        /// <param name="logEvent">The log event.</param>
        protected override void Write(LogEventInfo logEvent)
        {
            lock (this)
            {
                if (logEvent.Level == LogLevel.Info)
                {
                    if (logEvent.FormattedMessage.StartsWith("[Enter]"))
                    {
                        FReflectInsight.EnterMethod(logEvent.FormattedMessage.Replace("[Enter]", String.Empty));
                        return;
                    }
                    if (logEvent.FormattedMessage.StartsWith("[Exit]"))
                    {
                        FReflectInsight.ExitMethod(logEvent.FormattedMessage.Replace("[Exit]", String.Empty));
                        return;
                    }

                    SendMessage(MessageType.SendInformation, logEvent);
                }
                else if (logEvent.Level == LogLevel.Trace)
                {
                    SendMessage(MessageType.SendTrace, logEvent);
                }
                else if (logEvent.Level == LogLevel.Debug)
                {
                    SendMessage(MessageType.SendDebug, logEvent);
                }
                else if (logEvent.Level == LogLevel.Warn)
                {
                    SendMessage(MessageType.SendWarning, logEvent);
                }
                else if (logEvent.Level == LogLevel.Error)
                {
                    SendMessage(MessageType.SendError, logEvent);
                }
                else if (logEvent.Level == LogLevel.Fatal)
                {
                    SendMessage(MessageType.SendFatal, logEvent);
                }
                else // safetynet
                {
                    SendMessage(MessageType.SendMsg, logEvent);
                }
            }            
        }
    }     
}
